#pragma once

// 线程池的封装

#include <pthread.h>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "Log.hpp"
using namespace ThreadModule;

const static int gdefaultthreadnum = 5;
template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep() // 谁调用谁休眠
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeUp() // 唤醒一个线程
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeUpAll() // 唤醒全部线程
    {
        pthread_cond_broadcast(&_cond);
    }
    void HandlerTask(std::string name) // 第一个参数是this指针
    {
        while (true) // 一直处理任务，直到线程池退出且任务队列为空
        {
            LockQueue(); // 任务队列是临界资源，需要保护

            // 任务队列为空，且线程池还在运行，则线程休眠
            while (_task_queue.empty() && _isrunning)
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }

            // 任务队列为空，且线程池不运行了，退出
            if (_task_queue.empty() && !_isrunning)
            {
                UnlockQueue(); // 解锁
                break;         // 退出
            }

            // 还有任务没处理，则处理任务
            T t = _task_queue.front(); // 取出任务
            _task_queue.pop();
            UnlockQueue(); // 线程已经取出任务，任务已为线程私有，且任务可能比较耗时，解锁

            LOG(DEBUG, "%s get a task\n", name.c_str());

            t();

            //LOG(DEBUG, "%s handler a task,result is: %s", name.c_str(), t.ResultToString().c_str());
        }
    }

    // 单例模式下构造函数是私有的
    ThreadPool(int num = gdefaultthreadnum)
        : _threadnum(num), _waitnum(0), _isrunning(false)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        LOG(INFO, "ThreadPool Construct\n");
    }
    // 只是传了线程需要的参数，线程还没有创建出来
    void InitThreadPool()
    {
        for (int i = 0; i < _threadnum; i++)
        {
            std::string name = "thread-" + std::to_string(i + 1);

            // 由于 HandlerTask 的第一个参数是 this 指针，第二个参数是 string 类型
            // 而 Thread.hpp 中 Thread 类的构造函数要求传的函数的参数只能有 string 类型
            // 可以用 bind 函数对 HandlerTask 的第一个参数进行绑定（绑了 this 指针）
            // （_1 就是绑定第一个参数，_2 就是绑定第二个参数）
            // 绑定之后，使用 HandlerTask 就只需要传 string 类型的参数
            // 相当于函数的参数从( this,string )变成了( string )
            _threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1), name);
            LOG(INFO, "init thread %s done\n", name.c_str());
        }
        _isrunning = true;
    }

    // 创建线程
    void Start()
    {
        for (auto &thread : _threads)
        {
            thread.Start();
        }
    }

    // 禁用赋值和拷贝
    ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
    ThreadPool(const ThreadPool<T> &) = delete;

public:
    static ThreadPool<T> *GetInstance()
    {
        // 如果是第一个调用，则会创建单例
        if (_instance == nullptr)
        {
            //不在if外面加锁，是因为如果是第二次调用的话，不需要创建单例，不需要保护资源
            //只需要获取单例，所以只需要在第一次调用的时候加锁，既保证线程安全又降低加锁成本
            LockGuard lockguard(&_lock);

            //第二次判断，防止在阻塞等待锁的过程中已经由别的线程创建单例了
            //但由于本线程已经通过if判断了，所以再创建了一次
            if (_instance == nullptr)
            {
                _instance = new ThreadPool<T>();
                _instance->InitThreadPool();
                _instance->Start();

                LOG(DEBUG, "创建线程池单例成功\n");
            }
        }
        // 不是第一次调用，则直接获取单例，不需要重新创建
        else
        {
            LOG(DEBUG, "获取线程池单例成功\n");
        }
        return _instance;
    }
    void Stop()
    {
        LockQueue(); // 状态值也是临界资源，需要保护

        _isrunning = false;

        // 线程池不运行了，需要唤醒所以在条件变量下等待的线程
        // 否则线程会一直阻塞等待条件变量，无法被 join
        ThreadWakeUpAll();

        UnlockQueue();
    }

    void Wait()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
            LOG(INFO, "%s is quit...\n", thread.name().c_str());
        }
    }

    bool Enqueue(const T &t)
    {
        bool ret = false;
        LockQueue();

        // 线程池在运行中，才可以放入任务
        if (_isrunning)
        {
            _task_queue.push(t);

            // 有线程在等任务，唤醒线程
            if (_waitnum > 0)
                ThreadWakeUp();

            LOG(DEBUG, "enqueue task success\n");
            ret = true; // 任务插入成功
        }

        UnlockQueue();

        return ret;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _threadnum; // 线程的个数

    std::vector<Thread> _threads; // 管理线程
    std::queue<T> _task_queue;    // 任务队列

    pthread_mutex_t _mutex; // 互斥锁
    pthread_cond_t _cond;   // 信号量

    bool _isrunning; // 线程池的启动状态
    int _waitnum;    // 线程等待的个数

    // 添加单例模式
    static ThreadPool<T> *_instance;
    static pthread_mutex_t _lock;
};

// 初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;

template <typename T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;